//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/bbox/protocol/cpeerconnection.h>
#include <buola/bbox/csession.h>
#include <buola/bbox/detail/clink.h>

namespace buola { namespace bbox { namespace protocol {

CPeerConnection::CPeerConnection(io::PSocket pSocket)
    :   CSyncConnection(std::move(pSocket),"peer")
{
}

CPeerConnection::~CPeerConnection()
{
}

std::pair<uint64_t,uint64_t> CPeerConnection::ConnectTerminal(uint64_t pService,const std::string &pTerminal)
{
    CStackPacket lPacket(io::nHeader,EOpcode::CONNECTTERMINAL);
    lPacket << pService << pTerminal;
    uint64_t lLocalID=get_new_uid();
    lPacket << lLocalID;
    
    std::unique_ptr<CPacket> lReply(SendPacketAndWaitReply(lPacket));

    if(lReply->Header().IsError())
        throw XConnection("can't connect to terminal");

    uint64_t lRemoteID;
    *lReply >> lRemoteID;
    return {lLocalID,lRemoteID};
}

void CPeerConnection::RegisterLink(detail::CLink *pLink)
{
    mLinks.emplace(pLink->UID(),pLink);
}

void CPeerConnection::UnregisterLink(detail::CLink *pLink)
{
    mLinks.erase(pLink->UID());
}

void CPeerConnection::DoCallMethod(CPacket &pPacket)
{
    uint64_t lServiceID;
    pPacket >> lServiceID;

    detail::CServiceBase *lService=get_session().GetLocalService(lServiceID);

    if(!lService)
    {
        SendError(pPacket,EError::SERVICE_NOT_FOUND);
        return;
    }

    CReplyPacket lReply(pPacket);
    lService->BBoxCall(pPacket,lReply);
    SendPacket(lReply);
}

void CPeerConnection::DoConnectTerminal(CPacket &pPacket)
{
    uint64_t lServiceID;
    pPacket >> lServiceID;
    
    detail::CServiceBase *lService=get_session().GetLocalService(lServiceID);
    
    if(!lService)
    {
        SendError(pPacket,EError::SERVICE_NOT_FOUND);
        return;
    }
    
    CReplyPacket lReply(pPacket);
    lService->BBoxConnect(pPacket,lReply,this);
    SendPacket(lReply);
}

void CPeerConnection::DoLinkData(CPacket &pPacket)
{
    uint64_t lTerminalID;
    pPacket >> lTerminalID;

    detail::CLink *lLink=mLinks.at(lTerminalID);
    lLink->OnPacket(pPacket);
}

void CPeerConnection::OnPacket(CPacket &pPacket)
{
    switch(pPacket.Header().Major())
    {
    case EOpcode::CALLMETHOD:
        DoCallMethod(pPacket);
        break;
    case EOpcode::CONNECTTERMINAL:
        DoConnectTerminal(pPacket);
        break;
    case EOpcode::LINKDATA:
        DoLinkData(pPacket);
        break;
    default:
        msg_warn() << "received unknown packet with opcode=" << (int)pPacket.Header().Major() << "... ignoring it\n";
    }
}

void CPeerConnection::OnEOF()
{
}

/*namespace protocol*/ } /*namespace bbox*/ } /*namespace buola*/ }
